﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Reflection;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;



namespace Treasures
{

    public class ElementMap : Singleton<ElementMap>
    {
        #region imports for console and GetCursorPos

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern Boolean AllocConsole();

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern Boolean FreeConsole();

        [DllImport("user32.dll")]
        public static extern bool GetCursorPos(out Point lpPoint);

        #endregion

        #region Animation methods

        private void MakeAllBusy()
        {
            int length = ElementMap.Instance.BusyMap.GetLength(0);
            for(int i = 0; i < length; i++)
            {
                for(int j = 0; j < length; j++)
                {
                    ElementMap.Instance.BusyMap[i, j] = true;
                }
            }
        }

        public void MakeAllAccessible()
        {
            int length = ElementMap.Instance.BusyMap.GetLength(0);
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    ElementMap.Instance.BusyMap[i, j] = false;
                }
            }
        }

        private void AnimateMapOut()
        {
            int N = 25;
            int sleepTime = 10;
            int length = ElementMap.Instance.PointMap.GetLength(0);
            //make all busy
            ElementMap.Instance.MakeAllBusy();

           
            float delta = (Constants.initialElementWidth + Constants.distanceBetweenElements) / N;
            //wiggling
            for (int cnt = 0; cnt < N; cnt++)
            {
                for (int j = length - 1; j >= 0; j--)
                {
                    for (int i = length - 1; i >= 0; i--)
                    {
                        if (cnt % 2 == 0)
                        {
                            ElementMap.Instance.PointMap[j, i] = new PointF(ElementMap.Instance.PointMap[j, i].X + ElementMap.Instance.rnd.Next(-4, 4), ElementMap.Instance.PointMap[j, i].Y + ElementMap.Instance.rnd.Next(-4, 4));
                        }
                        else
                        {
                            ElementMap.Instance.PointMap[j, i] = new PointF(ElementMap.Instance.initialPointMap[j, i].X, ElementMap.Instance.initialPointMap[j, i].Y);
                        }
                    }

                }
                Thread.Sleep(sleepTime * 2);
            }
            //droping
            for (int cnt = 0; cnt < Convert.ToInt32(2 * length * N); cnt++)
            {
                for (int j = length - 1; j >= 0; j--)
                {
                    for (int i = length - 1; i >= 0; i--)
                    {
                        ElementMap.Instance.PointMap[j, i] = new PointF(ElementMap.Instance.PointMap[j, i].X, ElementMap.Instance.PointMap[j, i].Y + (float)Math.Pow((double)(1f + (float)(length - j) / 2f), 1.2) * delta + delta);
                    }
                    
                }
                Thread.Sleep(sleepTime);
            }
            //make all accessible
            ElementMap.Instance.MakeAllAccessible();
           
        }

        private void AnimateMapIn()
        {
            int length = ElementMap.Instance.PointMap.GetLength(0);
            //placing elements in sky
            ElementMap.Instance.MakeAllBusy();
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    ElementMap.Instance.PointMap[i, j] = new PointF(ElementMap.Instance.initialPointMap[i, j].X, ElementMap.Instance.initialPointMap[i, j].Y - length * (Constants.distanceBetweenElements + Constants.initialElementWidth));
                }
            } 

            int N = 18;
            int sleepTime = 15;
            float delta = (Constants.initialElementWidth + Constants.distanceBetweenElements) / N;
           
            for (int cnt = 0; cnt < Convert.ToInt32(length * N); cnt++)
            {
                for (int i = length - 1; i >= 0; i--)
                {
                    for (int j = 0; j < length; j++)
                    {
                        if (ElementMap.Instance.PointMap[i, j].Y < ElementMap.Instance.initialPointMap[i, j].Y - ((float)Math.Pow((double)(1f + (float)j / 2f), 1.2) * delta + delta))
                        {
                            ElementMap.Instance.PointMap[i, j] = new PointF(ElementMap.Instance.initialPointMap[i, j].X, ElementMap.Instance.PointMap[i, j].Y + (float)Math.Pow((double)(1f + (float)j / 2f), 1.2) * delta + delta);
                        }
                        if (ElementMap.Instance.PointMap[i, j].Y >= ElementMap.Instance.initialPointMap[i, j].Y - ((float)Math.Pow((double)(1f + (float)j / 2f), 1.2) * delta + delta) && ElementMap.Instance.PointMap[i, j].Y < ElementMap.Instance.initialPointMap[i, j].Y)
                        {
                            ElementMap.Instance.PointMap[i, j] = new PointF(ElementMap.Instance.initialPointMap[i, j].X, ElementMap.Instance.initialPointMap[i, j].Y);
                        }
                    }
                    if (ElementMap.Instance.PointMap[0, 0] == ElementMap.Instance.initialPointMap[0, 0])
                    {
                        break;
                    }
                }
                Thread.Sleep(sleepTime);
            }
            ElementMap.Instance.MakeAllAccessible();
        }

        private void SelectionAnim(ChipSelection sel, int rowIndex, int colIndex, int sleepTime)
        {
            int N = 20;
            sel.Scale = 2;
           
            if (sel.AnimatingPhase == 0)
            {
                sel.AnimatingPhase = 1;
                for (int cnt = 0; cnt < N; cnt++)
                {
                    if (sel.Scale > 1)
                    {
                        sel.Scale -= 0.05f;
                        Thread.Sleep(sleepTime);
                    }
                }
            }
            sel.AnimatingPhase = 0;
        }

      //  static int busyC = 0;
        public void DeleteFX(ChipFull[,] _map, List<IndexStruct> chips, int sleepTime, bool isOldMap)
        {
            int N = 14;
            int length = chips.Count;

            if (!isOldMap)
            {
                sleepTime = 0;
            }
            //sleepTime = 10;
            
            foreach (IndexStruct item in chips)
            {
                if (!ElementMap.Instance.BusyMap[item.i, item.j] && _map[item.i, item.j].Chip.AnimatingPhase == 0)
                {
                    _map[item.i, item.j].Chip.AnimatingPhase = 1;
                }
            }

            for (int cnt = 0; cnt < N; cnt++)
            {
                foreach (IndexStruct item in chips)
                {
                    _map[item.i, item.j].Chip.Scale += 0.01f;
                }
                Thread.Sleep(sleepTime);
            }

            for (int cnt = 0; cnt < N / 2; cnt++)
            {
                foreach (IndexStruct item in chips)
                {
                    _map[item.i, item.j].Chip.Scale -= 0.025f;
                }
                Thread.Sleep(sleepTime);
            }

            for (int cnt = 0; cnt < N / 2; cnt++)
            {
                foreach (IndexStruct item in chips)
                {
                    _map[item.i, item.j].Chip.Scale -= 0.085f;
                }
                Thread.Sleep(sleepTime / 2);
            }        

            for (int i = 0; i < _map.GetLength(0); i++)
            {
                for (int j = 0; j < _map.GetLength(0); j++)
                {
                    if (_map[i, j].MarkedForDeletion)
                    {
                        ElementMap.Instance.BusyMap[i, j] = true;
                    }
                    else
                    {
                        if (ElementMap.Instance.BusyMap[i, j])
                        {
                            //busyC++;
                            ElementMap.Instance.BusyMap[i, j] = false;
#if DEBUG
                         //   Console.WriteLine("BusyCount = {0}", busyC);
#endif
                        }
                    }
                }
            }

            foreach (IndexStruct item in chips)
            {
                _map[item.i, item.j].Chip.ElementColor = Constants.Colors[0];
                _map[item.i, item.j].Chip.AnimatingPhase = 0;
            }
        }

        public void SwapMotion(object obj)
        {
            if (ElementMap.Instance.ColorMap.Count == 0)
            {
                return;
            }
            ThreadData data = (ThreadData)obj;
            Point element1Index = data.element1Index;
            Point element2Index = data.element2Index;
            float[] point1Coords = new float[]
            {
                ElementMap.Instance.initialPointMap[element1Index.Y, element1Index.X].Y,
                ElementMap.Instance.initialPointMap[element1Index.Y, element1Index.X].X,
            };
            float[] point2Coords = new float[]
            {
                ElementMap.Instance.initialPointMap[element2Index.Y, element2Index.X].Y,
                ElementMap.Instance.initialPointMap[element2Index.Y, element2Index.X].X,
            };
            bool rollback = data.rollback;
            int N = 10;
            float delta = 0f;
            int sleepTime = 20;

            if (point1Coords[1] == point2Coords[1] && !MoveNoMore)
            {
                delta = (point2Coords[0] - point1Coords[0]) / N;
                {
                    if (rollback)
                    {
                        delta = -delta;
                    }

                    for (int i = 0; i < N; i++)
                    {

                        ElementMap.Instance.PointMap[element1Index.X, element1Index.Y] =
                            new PointF(ElementMap.Instance.PointMap[element1Index.X, element1Index.Y].X + delta,
                               ElementMap.Instance.PointMap[element1Index.X, element1Index.Y].Y);
                        ElementMap.Instance.PointMap[element2Index.X, element2Index.Y] =
                            new PointF(ElementMap.Instance.PointMap[element2Index.X, element2Index.Y].X - delta,
                               ElementMap.Instance.PointMap[element2Index.X, element2Index.Y].Y);

                        Thread.Sleep(sleepTime);
                    }
                }
            }
            if (point1Coords[0] == point2Coords[0] && !MoveNoMore)
            {
                delta = (point2Coords[1] - point1Coords[1]) / N;
                {
                    if (rollback)
                    {
                        delta = -delta;
                    }
                    for (int i = 0; i < N; i++)
                    {
                        ElementMap.Instance.PointMap[element1Index.X, element1Index.Y] =
                            new PointF(ElementMap.Instance.PointMap[element1Index.X, element1Index.Y].X,
                                ElementMap.Instance.PointMap[element1Index.X, element1Index.Y].Y + delta);
                        ElementMap.Instance.PointMap[element2Index.X, element2Index.Y] =
                            new PointF(ElementMap.Instance.PointMap[element2Index.X, element2Index.Y].X,
                                ElementMap.Instance.PointMap[element2Index.X, element2Index.Y].Y - delta);

                        Thread.Sleep(sleepTime);
                    }
                }
            }
        }

        //drop from sky
        public ChipFull[,] DropMotion(ChipFull[,] _map, int colIndex, int depthOfFall, int sleepTime, bool isOldMap)
        {

            int N = 30;
            float delta = 0f;
            if (!isOldMap)
            {
                sleepTime = 0;
                N = 1;
            }

            delta = (ElementMap.Instance.initialPointMap[0, colIndex].Y - ElementMap.Instance.PointMap[0, colIndex].Y) / N;
            //make elements busy
            for (int i = depthOfFall - 1; i >= 0; i--)
            {
                ElementMap.Instance.BusyMap[i, colIndex] = true;
            }

            //actual move
            for (int n = 0; n < N; n++)
            {
                for (int i = depthOfFall - 1; i >= 0; i--)
                {
                    ElementMap.Instance.BusyMap[i, colIndex] = true;
                    ElementMap.Instance.PointMap[i, colIndex] = new PointF(ElementMap.Instance.PointMap[i, colIndex].X, ElementMap.Instance.PointMap[i, colIndex].Y + delta);
                    #if DEBUG
                   // Console.WriteLine("New Y location: {0}", _map[i, colIndex].Location.Y);
                    #endif
                }
                Thread.Sleep(sleepTime / 2);
            }


            //make elements acesscible
            for (int i = depthOfFall - 1; i >= 0; i--)
            {
                ElementMap.Instance.PointMap[i, colIndex] = new PointF(ElementMap.Instance.initialPointMap[i, colIndex].X, ElementMap.Instance.initialPointMap[i, colIndex].Y);
                ElementMap.Instance.BusyMap[i, colIndex] = false;
            }

            return _map;
        }

         // coloumn drop
        public ChipFull[,] DropMotion(ChipFull[,] _map, int colIndex, int lowestIndexToFall, int depthOfFall, int sleepTime, bool isOldMap)
        {
            if (lowestIndexToFall == -1)
            {
                //MessageBox.Show(String.Format("(i = -1), {0}, {1}", colIndex, depthOfFall));
                return _map;
            }

            int N = 8;
            float delta = 0f;
            if (!isOldMap)
            {
                sleepTime = 0;
                N = 1;
            }

            delta = (ElementMap.Instance.initialPointMap[1, 0].Y - ElementMap.Instance.initialPointMap[0, 0].Y) / N;
            //make elements busy
            for (int i = lowestIndexToFall + depthOfFall; i >= 0; i--)
            {
                ElementMap.Instance.BusyMap[i, colIndex] = true;
            }

            //actual move
            for (int k = 0; k < depthOfFall; k++)
            {
                for (int n = 0; n < N; n++)
                {
                    for (int i = lowestIndexToFall; i >= 0; i--)
                    {
                        ElementMap.Instance.BusyMap[i + depthOfFall, colIndex] = true;
                        ElementMap.Instance.PointMap[i + depthOfFall, colIndex] = new PointF(ElementMap.Instance.PointMap[i + depthOfFall, colIndex].X, ElementMap.Instance.PointMap[i + depthOfFall, colIndex].Y + delta);
                        #if DEBUG
                      //  Console.WriteLine("New Y location: {0}", _map[i + depthOfFall, colIndex].Location.Y);
                        #endif
                    }
                    Thread.Sleep(sleepTime);
                }
            }
           //make elements acesscible
            for (int i = lowestIndexToFall + depthOfFall; i >= 0; i--)
            {
                ElementMap.Instance.BusyMap[i, colIndex] = false;
                ElementMap.Instance.PointMap[i, colIndex] = ElementMap.Instance.initialPointMap[i, colIndex];
            }
            return _map;
        }

        #endregion

        #region Fields and Props

        #region Fields and Props for Animation

        public Task[] dropCol = new Task[Constants.initialElementsInRow];
        private Task[] deleteAnim = new Task[Constants.initialElementsInRow * Constants.initialElementsInRow];

        public bool MoveNoMore { get; set; }

        #endregion

        public Task mainJob { get; private set; }

        public Stack<ChipFull[,]> ColorMap { get; set; }

        public readonly PointF[,] initialPointMap;

        public PointF[,] PointMap { get; private set; }

        public bool[,] BusyMap { get; private set; } 

        public Point Selection { get; set; }

        private Random rnd;

        public int NumberOfColours { get; private set; }

        private int MaximumCrystalsOnMap { get; set; }

        public LevelControl LControl { get; set; }

        private int CurrentCrystalsOnMap { get; set; }

        public event Action ChangeRefreshState;

        public event Action StartTimer;

        private int EmptySpots { get; set; }
        
        public ChipSelection SelFigure { get; private set; }

        public object syncObject;
        public object syncObject2;
        public object syncObject3;
       // private object[] syncObjects = new object[Constants.initialElementsInRow];

        #endregion Fields

        #region Constructor
        private ElementMap()
        {
            #if DEBUG
            /////Console
            AllocConsole();
            Console.WriteLine("Hello");
            #endif
            
            //events subcribing


            //.....
            #if DEBUG
            MainWindowForm.FreeAll += new Action(() => { FreeConsole(); });
            #endif

            //synchronization
            syncObject = new object();
            syncObject2 = new object();
            syncObject3 = new object();
            //for (int i = 0; i < syncObjects.GetLength(0); i++ )
            //{
            //    syncObjects[i] = new object();
            //}
            for (int i = 0; i < dropCol.GetLength(0); i++)
            {
                dropCol[i] = Task.Run(new Action(() => { }));
            }

            for (int i = 0; i < deleteAnim.GetLength(0); i++)
            {
                deleteAnim[i] = Task.Run(new Action(() => { }));
            }


            //basic fields
            this.NumberOfColours = Constants.initialNumberOfColours + 1;
            this.MaximumCrystalsOnMap = Constants.initialMaximumCrystalsOnMap;
            this.CurrentCrystalsOnMap = 0;
            this.LControl = new LevelControl();
            this.EmptySpots = 0;
            this.MoveNoMore = false;

            //maps
            this.ColorMap = new Stack<ChipFull[,]>();
            int h = Constants.initialElementsInRow;
            int w = Constants.initialElementsInRow;
            ChipFull[,] Map = new ChipFull[h, w];
            this.initialPointMap = new PointF[h, w];
            this.PointMap = new PointF[h, w];
            this.BusyMap = new bool[h, w];
            this.Selection = new Point(-1, -1);
            this.SelFigure = new ChipSelection();
            this.rnd = new Random();

            // Filling point map (generating new map)

            for (int i = 0; i < h; i++)
            {
                for (int j = 0; j < w; j++)
                {
                    Point loc = new Point(Constants.initialLocation.X + j * Constants.initialElementWidth + j * Constants.distanceBetweenElements
                       + Constants.initialFirstElementLocation.X + 1 + Constants.Border,
                       Constants.initialLocation.Y + i * Constants.initialElementHeight + i * Constants.distanceBetweenElements
                       + Constants.initialFirstElementLocation.Y + 1 + Constants.Border);
                    this.initialPointMap[i, j] = new Point(loc.X, loc.Y);
                    this.PointMap[i, j] = new Point(loc.X, loc.Y);
                    this.BusyMap[i, j] = false;
                    Map[i, j] = new ChipFull();
                    Map[i, j].Chip = new ChipFigure();
                    Map[i, j].index.i = i;
                    Map[i, j].index.j = j;
                }
            }

            #region for tests
            /*  for (int i = 0; i < Map.GetLength(0); i++)
            {
                if (i < Convert.ToInt32(Map.GetLength(0) / 2.0))
                {
                    Map[0, i] = Constants.Colors[1];
                }
                else 
                {
                    Map[0, i] = Constants.Colors[2];
                }
            }
            Map[0, 0] = Constants.Colors[3];
            Map[1, 0] = Constants.Colors[4];*/
            #endregion

            ///// !!!!!!!!!!!!!!!!!
            ColorMap.Push(Map);
        }

        #endregion Constructor

        #region core method

        public void ProcessUserAction(object sender, MouseEventArgs e) //invoked by event from gui thread 
        {
            try
            {
                if(ElementMap.Instance.AnyBusy())
                {
                   // MessageBox.Show("Busy");
                }
                mainJob = Task.Run(new Action(() =>
                {
                    DoubleBufferedPanel panel = (DoubleBufferedPanel)sender;
                    Point index = GetIndexByCoords(e.X, e.Y);
                    if(index.X == -1)
                    {
                        return;
                    }
                    if (index.X == -2)
                    {
                        if (!panel.Reset1Performed)
                        {
                            panel.Reset1Performed = true;
                            Thread th = new Thread(new ThreadStart(new Action(() =>
                            {
                                ElementMap.Instance.OnGenerateNewMap(true, false);
                            })));
                            th.Start();
                        }
                        return;
                    }
                    if(index.X == -3)
                    {
                        if (!panel.Reset2Performed)
                        {
                            panel.Reset2Performed = true;
                            Thread th = new Thread(new ThreadStart(new Action(() =>
                            {
                                ElementMap.Instance.OnGenerateNewMap(true, false);
                            })));
                            th.Start();
                        }
                        return;
                    }


                    if (ElementMap.Instance.MoveNoMore)
                    {
                        return;
                    }
                    lock (ElementMap.Instance.syncObject2)
                    {
                        if (ElementMap.Instance.ColorMap.Peek()[index.X, index.Y].Chip.ElementColor == Constants.Colors[0] || ElementMap.Instance.BusyMap[index.X, index.Y]
                            || ElementMap.Instance.ColorMap.Peek()[index.X, index.Y].MarkedForDeletion)
                        {
                            ElementMap.Instance.Selection = new Point(-1, -1);
                            return;
                        }
                        if (ElementMap.Instance.Selection.X == -1)
                        {
                            ElementMap.Instance.Selection = index;
                            Task.Run( new Action( () =>
                                {
                                    ElementMap.Instance.ColorMap.Peek()[index.X, index.Y].ClickedAsSelection = true;
                                    ElementMap.Instance.SelectionAnim(ElementMap.Instance.SelFigure, index.X, index.Y, 10);
                                }
                                ));
                            return;
                        }
                    }
                    if (ElementMap.Instance.Selection.X != -1)
                    {
                        if (ElementMap.Instance.Selection == index)
                        {
                            ElementMap.Instance.SelectionAnim(ElementMap.Instance.SelFigure, index.X, index.Y, 10);
                            return;
                        }
                        if (ElementMap.Instance.Selection != index && ElementMap.Instance.Check(ElementMap.Instance.Selection, index))
                        {
                            if (!ElementMap.Instance.BusyMap[index.X, index.Y] && !ElementMap.Instance.BusyMap[ElementMap.Instance.Selection.X, ElementMap.Instance.Selection.Y])
                            {
                                // Console.WriteLine("move {0}, {1}", ElementMap.Instance.ColorMap.Peek()[index.X, index.Y].IsBusy, ElementMap.Instance.ColorMap.Peek()[ElementMap.Instance.Selection.X, ElementMap.Instance.Selection.Y].IsBusy);
                                Point curSelection = ElementMap.Instance.Selection;
                                lock (ElementMap.Instance.syncObject2)
                                {
                                    ElementMap.Instance.BusyMap[curSelection.X, curSelection.Y] = true;
                                    ElementMap.Instance.ColorMap.Peek()[curSelection.X, curSelection.Y].ClickedAsSelection = false;
                                    ElementMap.Instance.BusyMap[index.X, index.Y] = true;
                                    ElementMap.Instance.Selection = new Point(-1, -1);
                                }
                                //ElementMap.SavePreviousState();

                                ElementMap.Instance.SwapMotion(new ThreadData { element1Index = curSelection, element2Index = index, rollback = false });
                                
                                //swap in real map
                                lock (ElementMap.Instance.ColorMap.Peek())
                                {
                                    ElementMap.Instance.SwapElements(ElementMap.Instance.ColorMap.Peek(), curSelection, index);
                                }
                                if (!ElementMap.Instance.CheckSequences(ElementMap.Instance.ColorMap.Peek(), false, false, false))
                                {
                                    ElementMap.Instance.SwapMotion(new ThreadData { element1Index = curSelection, element2Index = index, rollback = false });
                                    ElementMap.Instance.SwapElements(ElementMap.Instance.ColorMap.Peek(), curSelection, index);
                                    ElementMap.Instance.BusyMap[curSelection.X, curSelection.Y] = false;
                                    ElementMap.Instance.BusyMap[index.X, index.Y] = false;
                                }
                                else
                                {
                                    ElementMap.Instance.BusyMap[curSelection.X, curSelection.Y] = false;
                                    ElementMap.Instance.BusyMap[index.X, index.Y] = false;
                                    ElementMap.Instance.LControl.Streak = 1;
                                    ElementMap.Instance.CheckSequences(ElementMap.Instance.ColorMap.Peek(), true, true, true);
                                }
                                
                                if (!ElementMap.Instance.CheckForAvailableMoves(ElementMap.Instance.ColorMap.Peek()))
                                {
                                    #if DEBUG
                                    Console.WriteLine("After check");
                                    ElementMap.Cout(ElementMap.Instance.ColorMap.Peek());
                                    #endif
                                    if (ElementMap.Instance.LControl.CurrentCrystalScore < ElementMap.Instance.LControl.Level * 5)
                                    {
                                        MessageBox.Show("No moves. Generating new map");
                                        #if DEBUG
                                        Console.WriteLine("Generating");
                                        #endif
                                        lock (ElementMap.Instance.ColorMap)
                                        {
                                            ElementMap.Instance.ColorMap.Clear();
                                            ElementMap.Instance.ColorMap.Push(ElementMap.Instance.GenerateEntireMap());
                                        }
                                    }

                                }


                            }
                            else
                            {
                                return;
                            }
                        }
                        else
                        {
                            for (int i = 0; i < ElementMap.Instance.ColorMap.Peek().GetLength(0); i++)
                            {
                                for (int j = 0; j < ElementMap.Instance.ColorMap.Peek().GetLength(0); j++)
                                {
                                    ElementMap.Instance.ColorMap.Peek()[i, j].ClickedAsSelection = false;
                                }
                            }
                                ElementMap.Instance.Selection = index;
                            ElementMap.Instance.ColorMap.Peek()[index.X, index.Y].ClickedAsSelection = true;
                            ElementMap.Instance.SelectionAnim(ElementMap.Instance.SelFigure, index.X, index.Y, 10);
                        }
                    }

                    ElementMap.Instance.MakeAllAccessible();
                    ChangeRefreshState();
                    #if DEBUG
                    Console.WriteLine("Thread ended successfully");
                    #endif
                }));
           }
           catch(AggregateException ex)
           {
               #if DEBUG
               Console.WriteLine("Exception caught. {0}", ex.InnerException.Message);
               #endif
           }
        }

        #endregion

        #region auxilary methods

        public bool AnyBusy()
        {
           int length = ElementMap.Instance.BusyMap.GetLength(0);
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    if (ElementMap.Instance.BusyMap[i, j])
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public Point GetIndexByCoords(int x, int y)
        {
            if(ElementMap.Instance.ColorMap.Count < 1)
            {
                return new Point(-1, -1);
            }
            int i = 0;
            int j = 0;
            int length = ElementMap.Instance.initialPointMap.GetLength(0);

            if (y >= ElementMap.Instance.initialPointMap[length - 1, 0].Y + Constants.initialElementWidth 
                || x >= ElementMap.Instance.initialPointMap[0, length - 1].X + Constants.initialElementWidth)
            {
                if (y >= Constants.Reset1Loc.Y && y < Constants.Reset1Loc.Y + 80
                    && x >= Constants.Reset1Loc.X && x < Constants.Reset1Loc.X + 80
                    )
                {
                    return new Point(-2, -1);
                }

                if (y >= Constants.Reset2Loc.Y && y < Constants.Reset2Loc.Y + 80
                    && x >= Constants.Reset2Loc.X && x < Constants.Reset2Loc.X + 80)
                {
                    return new Point(-3, -1);
                }
                return new Point(-1, -1);
            }


            for (i = length - 1; i > 0; i--)
            {
                if (y >= ElementMap.Instance.initialPointMap[i, 0].Y)
                {
                    break;
                }
            }
            for (j = length - 1; j > 0; j--)
            {
                if (x >= ElementMap.Instance.initialPointMap[0, j].X)
                {
                    break;
                }
            }
            return new Point(i, j);
        }

        private int GetCrystalCountToGenerate(ChipFull[,] _map)
        {
            ElementMap obj = ElementMap.Instance;
            int ret = 0;
            lock (ElementMap.Instance.syncObject)
            {
                if (
                    obj.LControl.Level * 5 - obj.LControl.CurrentCrystalScore - obj.CurrentCrystalsOnMap > 1 &&
                    obj.EmptySpots > 5 &&
                    CountEmptySpotsInFirstRow(_map) > 1 &&
                    obj.MaximumCrystalsOnMap - obj.CurrentCrystalsOnMap > 1
                   )
                {
                    ret = 2;
                }

                if (
                    (obj.LControl.Level * 5 - obj.LControl.CurrentCrystalScore - obj.CurrentCrystalsOnMap == 1 ||
                    obj.EmptySpots < 6 ||
                    CountEmptySpotsInFirstRow(_map) == 1 ||
                    obj.MaximumCrystalsOnMap - obj.CurrentCrystalsOnMap == 1) &&
                    !(obj.LControl.Level * 5 - obj.LControl.CurrentCrystalScore - obj.CurrentCrystalsOnMap == 0 ||
                    obj.EmptySpots == 0 ||
                    CountEmptySpotsInFirstRow(_map) == 0 ||
                    obj.MaximumCrystalsOnMap - obj.CurrentCrystalsOnMap == 0)
                   )
                {
                    ret = 1;
                }
            }

            return ret;
        }

        private int CountEmptySpotsInFirstRow(ChipFull[,] _map)
        {
            int ret = 0;
            for (int i = 0; i < _map.GetLength(0); i++)
            {
                if (_map[0, i].Chip.ElementColor == Constants.Colors[0])
                {
                    ++ret;
                }
            }
            return ret;
        }

        private void CheckRow(ChipFull[] row, bool checkAndDelete, ref bool result)
        {
            if (row.Length > 3)
            {
                for (int i = 2; i < row.Length; i++)
                {
                    if (row[i].Chip.ElementColor.Equals(row[i - 1].Chip.ElementColor) && row[i].Chip.ElementColor.Equals(row[i - 2].Chip.ElementColor))
                    {
                        if (checkAndDelete)
                        {
                            row[i].MarkedForDeletion = true;
                            row[i - 1].MarkedForDeletion = true;
                            row[i - 2].MarkedForDeletion = true;
                        }
                        if (!result)
                        {
                            result = true;
                        }
                    }
                }
            }
        }

        private List<IndexStruct> FormChipListToAnimate(ChipFull[,] _map)
        {
            int length = _map.GetLength(0);
            List<IndexStruct> ret = new List<IndexStruct>();
            for (int row = 0; row < length; row++)
            {
                for (int col = 0; col < length; col++)
                {
                    if (_map[row, col].MarkedForDeletion && _map[row, col].Chip.AnimatingPhase == 0)
                    {
                        ret.Add(new IndexStruct { i = row, j = col });
                    }
                }
            }
            //if (ret.Count == 0)
            //{
            //    MessageBox.Show("s");
            //}
            return ret;
        }

        public bool IsFull(ChipFull[,] _map)
        {
            for (int i = 0; i < _map.GetLength(0); i++)
            {
                for (int j = 0; j < _map.GetLength(1); j++)
                {
                    if (_map[i, j].Chip.ElementColor == Color.Transparent)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        private Color GenerateNewColoumnInSky(ChipFull[,] _map, int depth, int colIndex, bool isOldMap, bool crystaled)
        {
            int crystalPos = ElementMap.Instance.rnd.Next(depth);


            int length = _map.GetLength(0);
            Color color = Color.Transparent;
            for (int i = depth - 1; i >= 0; i--)
            {
                int posFactor = i - length;
                color = Constants.Colors[ElementMap.Instance.rnd.Next(1, ElementMap.Instance.NumberOfColours)];
                ElementMap.Instance.PointMap[i, colIndex] = new PointF(ElementMap.Instance.initialPointMap[0, colIndex].X, posFactor * (Constants.initialElementWidth + Constants.distanceBetweenElements));
                if (i == crystalPos && crystaled)
                {
                    _map[i, colIndex] = new ChipFull(true, color,
                        new PointF(ElementMap.Instance.initialPointMap[0, colIndex].X, posFactor * (Constants.initialElementWidth + Constants.distanceBetweenElements)),
                        i, colIndex);
                    ElementMap.Instance.CurrentCrystalsOnMap++;
                }
                else
                {
                    _map[i, colIndex] = new ChipFull(false, color,
                        new PointF(ElementMap.Instance.initialPointMap[0, colIndex].X, posFactor * (Constants.initialElementWidth + Constants.distanceBetweenElements)),
                        i, colIndex);
                }
            }
            ElementMap.Instance.EmptySpots -= depth;
            if (isOldMap)
            {
                ElementMap.Instance.dropCol[colIndex].Wait();
                ElementMap.Instance.dropCol[colIndex] = Task.Run(new Action(() =>
                {
                    ElementMap.Instance.DropMotion(_map, colIndex, depth, 10/*10*/, isOldMap);
                }
                ));
            }
            else
            {
                ElementMap.Instance.DropMotion(_map, colIndex, depth, 0, isOldMap);
            }

            return color;
        }

        private int CountFirstEmptySpotsInColoumn(ChipFull[,] _map, int colIndex)
        {
            int i = 0;
            int length = _map.GetLength(0);
            for (i = 0; i < length; i++)
            {
                if (_map[i, colIndex].Chip.ElementColor != Constants.Colors[0])
                {
                    return i;
                }
            }
            return i;
        }

        private ChipFull[,] DropColoumn(ChipFull[,] _map, int colIndex, bool isOldMap)
        {
            int cnt = 0;
            for (int i = _map.GetLength(0) - 1; i >= 0; i--)
            {
                if (_map[i, colIndex].Chip.ElementColor == Constants.Colors[0])
                {
                    cnt++;
                }
                else
                {
                    if (cnt != 0)
                    {
                        //replace elements to correspond indexes
                        int depth = cnt;
                        /*  lock (_map)
                          {*/

                        for (int x = i; x >= 0; x--)
                        {
                            _map[x + cnt, colIndex].Chip.ElementColor = _map[x, colIndex].Chip.ElementColor;
                            _map[x + cnt, colIndex].Chip.IsCrystaled = _map[x, colIndex].Chip.IsCrystaled;
                            ElementMap.Instance.PointMap[x + cnt, colIndex] = ElementMap.Instance.initialPointMap[x, colIndex];
                            _map[x + cnt, colIndex].Chip.Scale = 1;
                        }
                        for (int x = 0; x < depth; x++)
                        {
                            _map[x, colIndex].Chip.ElementColor = Constants.Colors[0];
                        }
                        /*}*/
                        //Drop

                        int low = i;
                        if (isOldMap)
                        {
#if DEBUG
                          
                        //for (int hh = 0; hh < _map.GetLength(0); hh++ )
                        //{
                        //    Console.WriteLine("Look here {0} - {1}", hh, ElementMap.Instance.dropCol[hh].Status.ToString());
                        //}
#endif
                            ElementMap.Instance.dropCol[colIndex].Wait();
                            ElementMap.Instance.dropCol[colIndex] = Task.Run(new Action(() =>
                            {
                                DropMotion(_map, colIndex, low, depth, 10, isOldMap);
                                //    #if DEBUG
                                //    Console.WriteLine("dropCol[{0}] ended. ", colIndex);
                                //    //MessageBox.Show("ended");
                                //    #endif
                            }
                             ));

                        }
                        else
                        {
                            DropMotion(_map, colIndex, low, depth, 0, isOldMap);
                        }
                    }
                    cnt = 0;
                }
            }
            return _map;
        }

        #endregion

        #region public methods

        //Checking for elements to be close
        public bool Check(Point selection, Point itemToCheck)
        {
            if ((Math.Abs(selection.X - itemToCheck.X) == 1 &&
                selection.Y - itemToCheck.Y == 0) ||
                (Math.Abs(selection.Y - itemToCheck.Y) == 1 &&
                selection.X - itemToCheck.X == 0))
            {
               //MessageBox.Show("True");
               return true;
            }
            
            //MessageBox.Show("False");
            return false;
        }

        private void SavePreviousState()
        {
            ChipFull[,] curMap = new ChipFull[ElementMap.Instance.ColorMap.Peek().GetLength(0), ElementMap.Instance.ColorMap.Peek().GetLength(1)];

            // deep copy
            for (int i = 0; i < curMap.GetLength(0); i++)
            {
                for (int j = 0; j < curMap.GetLength(1); j++)
                {
                    curMap[i, j] = (ChipFull)ElementMap.Instance.ColorMap.Peek()[i, j].Clone();
                }
            }
            ElementMap.Instance.ColorMap.Push(curMap);
        }

        private void SwapElements(ChipFull[,] _map, Point e1, Point e2)
        {
            int tmpi = 0;
            int tmpj = 0;
            PointF tmpPoint = new PointF(-1f, -1f);
            ChipFull tmp;
            lock (_map[e1.X, e1.Y])
            {
                lock (_map[e2.X, e2.Y])
                {
                    tmp = _map[e2.X, e2.Y];
                    ElementMap.Instance.PointMap[e1.X, e1.Y] = ElementMap.Instance.initialPointMap[e1.X, e1.Y];
                    ElementMap.Instance.PointMap[e2.X, e2.Y] = ElementMap.Instance.initialPointMap[e2.X, e2.Y];

                    _map[e2.X, e2.Y] = _map[e1.X, e1.Y];
                    _map[e1.X, e1.Y] = tmp;
                    tmpi = e1.Y;
                    tmpj = e1.X;
                }
            }
            _map[e1.X, e1.Y].index.i = _map[e2.X, e2.Y].index.i;
            _map[e1.X, e1.Y].index.j = _map[e2.X, e2.Y].index.j;
            _map[e2.X, e2.Y].index.i = tmpi;
            _map[e2.X, e2.Y].index.j = tmpj;

        }
        
        //!!!!!!!!!!!!!!!!!!!!!!
        public bool CheckSequences(ChipFull[,] _map, bool checkAndDelete, bool addPoints, bool addCrystals) // calls FormDeletionMap, DeleteElements 
        {
            
            bool result = false;
            int length = _map.GetLength(0);
            Color color = Color.Transparent;
            ChipFull[] row = new ChipFull[_map.GetLength(0)];

            #region check every row

            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    row[j] = _map[i, j];
                }
                ElementMap.Instance.CheckRow(row, checkAndDelete, ref result);
            }

            #endregion

            #region check every coloumn

            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    row[j] = _map[j, i];
                }
                ElementMap.Instance.CheckRow(row, checkAndDelete, ref result);
            }

            #endregion

            #region save in file
#if DEBUG
            //using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"D:\EPAM lessons\IndividualProject\tests\DelMap.txt"))
            //{
            //    int cnt1 = 0;
            //    foreach (ChipFull b in _map)
            //    {
            //        file.Write(String.Format("{0}, ", b.MarkedForDeletion.ToString()));
            //        cnt1++;
            //        if (cnt1 > length - 1)
            //        {
            //            file.WriteLine();
            //            cnt1 = 0;
            //        }
            //    }
            //}
#endif
            #endregion

         
            //MessageBox.Show(result.ToString());
            if (result && checkAndDelete && (!ElementMap.Instance.MoveNoMore || !addPoints))
            {
               DeleteElements(_map, addPoints, addCrystals);
            }

            return result;
        }

        private void DeleteElements(ChipFull[,] _map, bool addPoints, bool addCrystals) // calls DropElements(), CheckSequences();
        {
            int length = _map.GetLength(0);
           

            //Visual FX of deletion + setting   Color.Transparent!!!!!!!!!!!!!!!
            Task.WaitAll(ElementMap.Instance.dropCol);
            //#if DEBUG
            //for (int i = 0; i < _map.GetLength(0); i++ )
            //{
            //    Console.WriteLine("Look here {0} - {1}", i, ElementMap.Instance.dropCol[i].Status.ToString());
            //}
            //#endif
                // MessageBox.Show("dds");

            List<IndexStruct> chipsToAnimate = ElementMap.Instance.FormChipListToAnimate(_map);

            if (addPoints && chipsToAnimate.Count > 0)
            {
                ElementMap.Instance.LControl.CountPoints(_map, chipsToAnimate);
                ElementMap.Instance.DeleteFX(_map, chipsToAnimate, 10, addPoints);
            }

            foreach (IndexStruct item in chipsToAnimate)
            {
                _map[item.i, item.j].Chip.ElementColor = Constants.Colors[0];
            }
            if (addPoints)
            {
                Thread.Sleep(100);
            }
            
            //
            for (int i = 0; i < length; i++)
            {
                for (int j = 0; j < length; j++)
                {
                    if (_map[i, j].MarkedForDeletion)
                    {
                        _map[i, j].MarkedForDeletion = false;
                        ElementMap.Instance.BusyMap[i, j] = false;
                        ElementMap.Instance.EmptySpots++;

                        if (_map[i, j].Chip.IsCrystaled)
                        {
                            //!!!!!!!!!!!!!!!!!????????????????????
                            lock (ElementMap.Instance.syncObject)
                            {
                                if (ElementMap.Instance.LControl.CurrentCrystalScore < ElementMap.Instance.LControl.Level * 5)
                                {
                                    ElementMap.Instance.LControl.CurrentCrystalScore++;
                                }
                                ElementMap.Instance.CurrentCrystalsOnMap--;
                                if(ElementMap.Instance.CurrentCrystalsOnMap < 0)
                                {
                                    ElementMap.Instance.CurrentCrystalsOnMap = 0;
                                    MessageBox.Show("CurrentCrystalsOnMap < 0");
                                }
                            }
                        }

                    }
                }
            }

            if (!ElementMap.Instance.MoveNoMore || !addPoints)
            {
                //Cout(_map);
             //   MessageBox.Show("fd");
                Task.WaitAll(ElementMap.Instance.dropCol);
                ElementMap.Instance.DropElements(_map, addPoints, addCrystals);
                #if DEBUG
               // Cout(_map);
                #endif
                ElementMap.Instance.CheckSequences(_map, true, addPoints, false);
            }
        }

        public void DropElements(ChipFull[,] _map, bool isOldMap, bool addCrystals) // calls isFull(), DropElements() (recursively), GenerateNew(), DropColoumn
        {
            //Console
            #if DEBUG
           // Cout(_map);
            #endif
            int mapLength = _map.GetLength(0);
            bool[] isCompleted = new bool[mapLength];
            int j = 0;
            for(j = 0; j < _map.GetLength(1); j++)
            {
                ElementMap.Instance.DropColoumn(_map, j, isOldMap);
            }

            for (int i = 0; i < Constants.initialElementsInRow; i++)
            {
                #if DEBUG
               // Console.WriteLine("col[{0}] status: {1}", i, ElementMap.Instance.dropCol[i].Status.ToString());
                #endif
            }
            Task.WaitAll(ElementMap.Instance.dropCol);
            #if DEBUG
           // Cout(_map);
            
            Console.WriteLine("Trying to generate new");
             
            //for (int i = 0; i < Constants.initialElementsInRow; i++)
            //{
            //    Console.WriteLine("col[{0}] status: {1}", i, ElementMap.Instance.dropCol[i].Status.ToString());
            //}
            #endif 
            Thread.Sleep(100);
            while(ElementMap.Instance.AnyBusy())
            {
                Thread.Sleep(100);
            }
            
            int n = 0;
            if(!isOldMap)
            {
                GenerateNewFromSky(_map, isOldMap, false, 0);
            }
            else
            {
                n = ElementMap.Instance.GetCrystalCountToGenerate(_map);
                if(n > 0)
                {
                    GenerateNewFromSky(_map, isOldMap, true, n);
                }
                else 
                {
                    GenerateNewFromSky(_map, isOldMap, false, 0);
                }
            }
            
            Task.WaitAll(ElementMap.Instance.dropCol);
           // Cout(_map);


            if (!ElementMap.Instance.IsFull(_map) && !ElementMap.Instance.MoveNoMore)
            {
                // MessageBox.Show("not full");
                addCrystals = false;
                DropElements(_map, isOldMap, addCrystals);
            }
           
        }

        private void GenerateNewFromSky(ChipFull[,] _map, bool isOldMap, bool crystaled, int crystalCount)
        {
            int length = _map.GetLength(0);
            //generating new crystals
            if (isOldMap && crystaled && crystalCount > 0)
            {
                int emptyCount = ElementMap.Instance.CountEmptySpotsInFirstRow(_map);
                int pos = 0;
                BitArray posArr = new BitArray(length);
                //defining crystal positions in coloumns as bitArray
                for (int i = 0; i < crystalCount; i++)
                {
                    pos = ElementMap.Instance.rnd.Next(length);
                    while(posArr.Get(pos) || _map[0, pos].Chip.ElementColor != Constants.Colors[0])
                    {
                        pos = ElementMap.Instance.rnd.Next(length);
                    }
                    posArr.Set(pos, true);
                }


                for (int j = 0; j < length; j++)
                {
                    int n = ElementMap.Instance.CountFirstEmptySpotsInColoumn(_map, j);
                    if (posArr.Get(j))
                    {
                        ElementMap.Instance.GenerateNewColoumnInSky(_map, n, j, isOldMap, true);
                    }
                    else
                    {
                        ElementMap.Instance.GenerateNewColoumnInSky(_map, n, j, isOldMap, false);
                    }
                }
            }
            //no crystals
            else
            {
                for (int j = 0; j < length; j++)
                {
                    int n = ElementMap.Instance.CountFirstEmptySpotsInColoumn(_map, j);
                    ElementMap.Instance.GenerateNewColoumnInSky(_map, n, j, isOldMap, false);
                }
            }
        }

        public void OnGenerateNewMap(bool sleep, bool newLevel)
        {
            while ((!ElementMap.Instance.IsFull(ElementMap.Instance.ColorMap.Peek()) || ElementMap.Instance.AnyBusy()  
                || ElementMap.Instance.CheckSequences(ElementMap.Instance.ColorMap.Peek(), false, false, false) || ElementMap.Instance.ColorMap.Count == 0) && !newLevel)
            {
                Thread.Sleep(50);
            }
            Task.WaitAll(ElementMap.Instance.dropCol);
            if (sleep)
            {
                ElementMap.Instance.AnimateMapOut();
            }
            if(newLevel)
            {
                if (ElementMap.Instance.LControl.Level == 6)
                {
                    ElementMap.Instance.NumberOfColours++;
                }
                if (ElementMap.Instance.LControl.Level == 11)
                {
                    ElementMap.Instance.NumberOfColours++;
                }
            }
            lock (ElementMap.Instance.ColorMap)
            {
                ElementMap.Instance.ColorMap.Clear();
                ElementMap.Instance.ColorMap.Push(ElementMap.Instance.GenerateEntireMap());
            }
            ElementMap.Instance.MoveNoMore = false;
            AnimateMapIn();
            if (newLevel)
            {
                ElementMap.Instance.StartTimer(); 
            }
          
        }

        private ChipFull[,] GenerateEntireMap()
        {
            ChipFull[,] NewlyGeneratedMap = new ChipFull[Constants.initialElementsInRow, Constants.initialElementsInRow];
            //filling it with Color.Transparent elements
            for(int i = 0; i < NewlyGeneratedMap.GetLength(0); i++)
            {
                for(int j = 0; j < NewlyGeneratedMap.GetLength(0); j++)
                {
                    NewlyGeneratedMap[i, j] = new ChipFull(false, 
                        Constants.Colors[ElementMap.Instance.rnd.Next(1, ElementMap.Instance.NumberOfColours)], 
                        ElementMap.Instance.initialPointMap[i, j], 
                        i, 
                        j);
                }
            }
            CheckSequences(NewlyGeneratedMap, true, false, false); // 2- true
               
            if (!CheckForAvailableMoves(NewlyGeneratedMap))
            {
               NewlyGeneratedMap = GenerateEntireMap();
            }

            //initializing other fields
            ElementMap.Instance.CurrentCrystalsOnMap = 0;
            ElementMap.Instance.Selection = new Point(-1, -1);
            return NewlyGeneratedMap;
        }
  
        public bool CheckForAvailableMoves(ChipFull[,] _map)
        {
            lock (ElementMap.Instance.syncObject3)
            {
                //Cheking by X
                for (int i = 0; i < _map.GetLength(0); i++)
                {
                    for (int j = 0; j < _map.GetLength(1) - 1; j++)
                    {
                        SwapElements(_map, new Point(i, j), new Point(i, j + 1));
                        if (CheckSequences(_map, false, false, false))
                        {
                            // MessageBox.Show(String.Format("True, {0}.{1}", i, j));
                            SwapElements(_map, new Point(i, j), new Point(i, j + 1));
                            return true;
                        }
                        SwapElements(_map, new Point(i, j), new Point(i, j + 1));
                    }
                }
                //Cheking by Y
                for (int i = 0; i < _map.GetLength(0); i++)
                {
                    for (int j = 0; j < _map.GetLength(1) - 1; j++)
                    {
                        SwapElements(_map, new Point(j, i), new Point(j + 1, i));
                        if (CheckSequences(_map, false, false, false))
                        {
                            // MessageBox.Show(String.Format("True, {0}.{1}", j, i));
                            SwapElements(_map, new Point(j, i), new Point(j + 1, i));
                            return true;
                        }
                        SwapElements(_map, new Point(j, i), new Point(j + 1, i));
                    }
                }
                // MessageBox.Show(String.Format("False"));
                return false;
            }
        }

        #endregion public methods

        #region debug output method (console)
        #if DEBUG
        public static void Cout(ChipFull[,] _map)
        {
            Console.WriteLine("-------------------------------------------------");
            string str = String.Empty;
            for (int i = 0; i < _map.GetLength(0); i++)
            {
                for (int j = 0; j < _map.GetLength(1); j++)
                {
                    if (_map[i, j].Chip.ElementColor == Color.Transparent)
                        str = "   ";
                    if (_map[i, j].Chip.ElementColor == Color.Blue)
                        str = "Blu";
                    if (_map[i, j].Chip.ElementColor == Color.Red)
                        str = "Red";
                    if (_map[i, j].Chip.ElementColor == Color.Green)
                        str = "Grn";
                    if (_map[i, j].Chip.ElementColor == Color.Orange)
                        str = "Orn";
                    if (_map[i, j].Chip.ElementColor == Color.Yellow)
                        str = "Yel";
                    if (_map[i, j].Chip.ElementColor == Color.White)
                        str = "Wht";
                    if (_map[i, j].Chip.ElementColor == Color.Violet)
                        str = "Vlt";
                    if (_map[i, j].Chip.ElementColor == Color.Gray)
                        str = "Gra";
                    if (_map[i, j].Chip.ElementColor == Color.Aqua)
                        str = "Aqa";
                    Console.Write("{0,5}", str);
                }
                Console.WriteLine();
            }
          //  Console.ReadLine();
        }
        #endif
        #endregion
    }


    public struct IndexStruct
    {
        public int i;
        public int j;
    }

    public class Singleton<T> where T : class
    {
        private static T _instance;

        static Singleton()
        {
        }
        protected Singleton()
        {
        }

        private static T CreateInstance()
        {
            ConstructorInfo ci = typeof(T).GetConstructor(
            BindingFlags.Instance | BindingFlags.NonPublic,
            null,
            new Type[0],
            new ParameterModifier[0]);
            
            return (T)ci.Invoke(null);
        }

        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = CreateInstance();
                     //MessageBox.Show("Created", "Info", MessageBoxButtons.OK);
                }
                return _instance;
            }
        }
    }
}
